﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Xml;
using System.Xml.Serialization;
using Defecter.Contract;
using DefectDBTypes;

namespace Defecter.Server
{
	#region Class ClientContractImpl
	/// <summary>
	/// Implements IClientContrct
	/// </summary>
	/// <remarks>
	/// </remarks>
//	[ServiceContract(CallbackContract=typeof(IClientCallbackContract))]
	[System.ServiceModel.ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single,
		InstanceContextMode=InstanceContextMode.Single,
		UseSynchronizationContext=false)]
	public class ClientContractImpl: IClientContract
	{
		#region ClientContractImpl - Types
		#endregion

		#region ClientContractImpl - Fields
		static private ClientContractImpl		cci_instance;
		private ServiceHost					cci_service_host;
		private SessionCollection		cci_sessions = new SessionCollection();
		#endregion

		#region ClientContractImpl - Delegates and Events
		#endregion

		#region ClientContractImpl - Properties
		static public ClientContractImpl Instance
		{
			get
			{
				return cci_instance;
			}
		}		/* property ClientContractImpl Instance */

		public SessionCollection Sessions
		{
			get
			{
				return cci_sessions;
			}
		}		/* property ClientContractImpl Sessions */
		#endregion

		#region ClientContractImpl - Constructors
		public ClientContractImpl ()
			: base()
		{
			cci_instance = this;
			// read database connection string etc
		}		/* constructor ClientContractImpl */
		#endregion

		#region ClientContractImpl - Methods
		/// <summary>
		/// Activate the WCF channel for client connections
		/// </summary>
		/// <param name="container"></param>
		/// <param name="callingType"></param>
		/// <param name="maxRetryCount"></param>
		static public void Activate ()
		{
			ServiceHost																host;
			int																				retry_count = 1;

/*
 *			Because the Model dll does not have any code, it does not get loaded.
 *			It must be loaded so that it will be searched for the meta data.
 *			So load it.
 * */
			System.Reflection.Assembly.Load("DefectDB");
			try
			{
				host = new ServiceHost(typeof(ClientContractImpl));		// calls the constructor
				cci_instance.cci_service_host = host;
			}
			catch (Exception ex)
			{
				throw;
			}
			cci_instance.cci_service_host.Faulted += new EventHandler(Instance.ChannelFaulted);
		retry_wcf:
			try
			{
				cci_instance.cci_service_host.Open(TimeSpan.FromSeconds(1*retry_count));
			}
			catch (TimeoutException tex)
			{
				if (retry_count++ < 3)
				{
					if (DefecterService.Instance.IsService)
						DefecterService.Instance.RequestAdditionalTime((1*1000)*retry_count);
					goto retry_wcf;
				}
				throw;
			}
			catch (Exception ex)
			{
				if (retry_count++ < 3)
				{
					if (DefecterService.Instance.IsService)
						DefecterService.Instance.RequestAdditionalTime(250);
					System.Threading.Thread.Sleep(250);
					goto retry_wcf;
				}
				throw;
			}
		}		/* method ClientContractImpl Activate */

		private void ChannelFaulted (object sender, EventArgs e)
		{
			string																		msg;

//			msg = ToString() + " channel for IClientContract faulted";
			//			throw new NotImplementedException(msg);
		}		/* method ClientContractImpl ClientConnectionFaulted */

		public void DeActivate ()
		{
			if (cci_service_host != null)
			{
				cci_service_host.Faulted -= new EventHandler(ChannelFaulted);
				try
				{
					cci_service_host.Close(new TimeSpan(0, 0, 5));
				}
				catch (Exception ex)
				{
					Console.WriteLine("Exception shutting down WCF channel for IClientContract");
					Console.WriteLine(ex);
				}
				((IDisposable)cci_service_host).Dispose();
				cci_service_host = null;
			}
		}		/* method ClientContractImpl DeActivate */
		#endregion

		#region ClientContractImpl - Methods - IClientContract

		#region ClientContractImpl - Methods - IClientContract - Housekeeping
		/// <summary>
		/// Creates an entry in the session table on the server
		/// </summary>
		/// <param name="sessionId"></param>
		/// <returns></returns>
		public string Attach (out Guid sessionId)
		{
			Session																		s = new Session();

			Sessions.Add(s);
			sessionId = s.Id;
			return null;
		}		/* method ClientContractImpl Attach */

		/// <summary>
		/// Releases the session id and associated information in the server
		/// </summary>
		/// <param name="sessionId"></param>
		/// <returns></returns>
		public string Detach (Guid sessionId)
		{
			try
			{
				Sessions.Remove(sessionId);
			}
			catch (Exception)
			{
			}
			return null;
		}		/* method ClientContractImpl Detach */
		#endregion

		#region ClientContractImpl - Methods - IClientContract - People
		public List<Client> GetClients (Guid sessionId, string [] includeRelations)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				var														clients = context.People.OfType<Client>();
				foreach (string s in includeRelations)
					clients = clients.Include(s);
				return clients.ToList();
			}
		}		/* method ClientContractImpl GetClients */

		public List<Developer> GetDevelopers (Guid sessionId, string[] includeRelations)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				var														Developer = context.People.OfType<Developer>();
				foreach (string s in includeRelations)
					Developer = Developer.Include(s);
				return Developer.ToList();
			}
		}		/* method ClientContractImpl GetDevelopers */

		public List<Person> GetPeople (Guid sessionId, string[] includeRelations)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				var														people = context.People.OfType<Person>();

				foreach (string s in includeRelations)
					people = people.Include(s);
				return people.ToList();
			}
		}		/* method ClientContractImpl GetPeople */

		public List<Manager> GetManagers (Guid sessionId, string[] includeRelations)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				var														managers = context.People.OfType<Manager>();
				foreach (string s in includeRelations)
					managers = managers.Include(s);
				return managers.ToList();
			}
		}		/* method ClientContractImpl GetManagers */

		public void UpdatePerson (Guid sessionId, Person updatedPerson)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				context.People.ApplyChanges(updatedPerson);
				context.SaveChanges();
			}
		}		/* method ClientContractImpl UpdatePerson */

		#endregion

		#region ClientContractImpl - Methods - IClientContract - Trouble_Report
#if false
		public List<Trouble_Report> GetTroubleReports (Guid sessionId)
		{
			Session													s = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				return context.Trouble_Report.ToList();
			}
		}		/* method ClientContractImpl GetTroubleReports */
#endif
		public List<Trouble_Report> GetTroubleReports (Guid sessionId, string objectSetType,
			string [] includedRelations)
		{
			Session																		session = Sessions[sessionId];
			List<Trouble_Report>											tr_list;

			using (DefectDBContainer context = new DefectDBContainer())
			{
				var tr_query = context.Trouble_Report.OfType<Trouble_Report>();
				foreach (string s in includedRelations)
					tr_query = tr_query.Include(s);

				if (objectSetType == "Person")
				{
					tr_list = tr_query.ToList();
				}
				else
					if (objectSetType == "Client")
					{
						var trs = from tr in tr_query
											where tr.Person is Client
											select tr;
						tr_list = trs.ToList();
					}
					else
						if (objectSetType == "Developer")
						{
							var trs = from tr in tr_query
												where tr.Person is Developer
												select tr;
							tr_list = trs.ToList();
						}
						else
							if (objectSetType == "Manager")
							{
								var trs = from tr in tr_query
													where tr.Person is Manager
													select tr;
								tr_list = trs.ToList();
							}
							else
								throw new Exception("Invalid ObjectSet type");
				}
			return tr_list;
		}		/* method ClientContractImpl GetTroubleReports */

		public List<Trouble_Report> GetTroubleReportsBy (Guid sessionId,
			IObjectWithChangeTracker associatedEntity)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				return context.Trouble_Report.Include("Person").ToList();
			}
		}		/* method ClientContractImpl GetTroubleReportsBy */

		public void UpdateTroubleReport (Guid sessionId, Trouble_Report updatedReport)
		{
			Session													session = Sessions[sessionId];

			using (DefectDBContainer context = new DefectDBContainer())
			{
				context.Trouble_Report.ApplyChanges(updatedReport);
				context.SaveChanges();
			}
		}		/* method ClientContractImpl UpdateTroubleReport */
		#endregion


		#region ClientContractImpl - Methods - IClientContract - User
		/// <summary>
		/// Changes the password for the currently logged on user
		/// </summary>
		/// <param name="userId"></param>
		/// <param name="currentPassword"></param>
		/// <param name="newPassword"></param>
		/// <returns>null if successful; otherwise error message</returns>
		public string ChangePassword (Guid sessionId, string userId, string currentPassword,
			string newPassword)
		{
			return null;
		}		/* method ClientContractImpl ChangePassword */

		/// <summary>
		/// Logs off the current user
		/// </summary>
		/// <returns>null if successful; otherwise error message</returns>
		public string Logoff (Guid sessionId)
		{
			return null;
		}		/* method ClientContractImpl Logoff */

		/// <summary>
		/// Logs on a user
		/// </summary>
		/// <param name="userId"></param>
		/// <param name="password"></param>
		/// <param name="userRole"></param>
		/// <returns>null if successful; otherwise error message</returns>
		public string Logon (Guid sessionId, string userId,
			string password, out UserRole userRole)
		{
			Session													s = Sessions[sessionId];
			string													result = s.LogOn(userId,password);

			if (result != null)
				userRole = s.User.UserRole;
			else
				userRole = UserRole.None;
			return result;
			return null;
		}		/* method ClientContractImpl Logon */

		/// <summary>
		/// Used by an manager to perform user maintenance
		/// </summary>
		/// <param name="action"></param>
		/// <param name="userId"></param>
		/// <param name="newPassword"></param>
		/// <param name="role"></param>
		/// <returns>null if successful; otherwise error message</returns>
		/// <remarks>
		/// Only the role and email address may be retrieved
		/// Password, role and email address may be updated
		/// </remarks>
		public string UserIdMaintance (Guid sessionId, RecordAction action, string userId,
			string newPassword, ref string emailAddress, ref UserRole role)
		{
			return null;
		}		/* method ClientContractImpl UserIdMaintance */
		#endregion
		#endregion
	}			/* class ClientContractImpl */
	#endregion
	
}